home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 1.0 beta / flock-1.0RC3.en-US.win32.exe / flock / components / flockPollerService.js < prev    next >
Text File  |  2007-10-18  |  16KB  |  540 lines

  1. // BEGIN FLOCK GPL
  2. // 
  3. // Copyright Flock Inc. 2005-2007
  4. // http://flock.com
  5. // 
  6. // This file may be used under the terms of of the
  7. // GNU General Public License Version 2 or later (the "GPL"),
  8. // http://www.gnu.org/licenses/gpl.html
  9. // 
  10. // Software distributed under the License is distributed on an "AS IS" basis,
  11. // WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. // for the specific language governing rights and limitations under the
  13. // License.
  14. // 
  15. // END FLOCK GPL
  16.  
  17. const PS_CONTRACTID = '@flock.com/poller-service;1';
  18. const PS_CLASSID    = Components.ID('{a2b4cd0e-804b-4578-8c3c-4b61f8c1047a}');
  19. const PS_CLASSNAME  = 'Flock Poller';
  20.  
  21.  
  22. const PREF_POLLER_BRANCH            = 'flock.poller'
  23. const PREF_REFRESH_DELAY            = '.refreshDelay';
  24. const PREF_REFRESH_TIMEOUT          = '.refreshTimeout';
  25. const PREF_DEFAULT_REFRESH_INTERVAL = '.defaultRefreshInterval';
  26. const PREF_MAX_CONCURRENT_REFRESHES = '.maxConcurrentRefreshes';
  27.  
  28. // According to Mozilla docs, timers can't be set for more than approximately
  29. // 6 hours. We'll limit to 4 hours just to be conservative.
  30. const MAX_TIMER_INTERVAL = 4 * 60 * 60 * 1000;
  31.  
  32. // Delay actual polling for 30 seconds after component initialization, so
  33. // there is reduced contention among other browser startup operations
  34. const STARTUP_DELAY = 30 * 1000;
  35.  
  36.  
  37. const Ci = Components.interfaces;
  38. const Cc = Components.classes;
  39. const Cr = Components.results;
  40.  
  41.  
  42. function getObserverService() {
  43.   return Cc['@mozilla.org/observer-service;1']
  44.     .getService(Ci.nsIObserverService);
  45.  
  46.  
  47. function RefreshListener(poller, urn, serviceId) {
  48.   this._poller = poller;
  49.   this._urn = urn;
  50.   this._serviceId = serviceId;
  51. }
  52.  
  53. RefreshListener.prototype = {
  54.   onResult: function PSL_onResult() { 
  55.     this._poller._logger.info('Successful refresh for ' + this._urn);
  56.     this._poller._finishedRefresh(this._urn);
  57.   },
  58.   onError: function PSL_onError(error) {
  59.     this._poller._logger.error('The service ' + this._serviceId + ' returned an ' +
  60.                                'error while refreshing ' + this._urn);
  61.     this._poller._finishedRefresh(this._urn);
  62.   },
  63.   notify: function PSL_notify(timer) {
  64.     this._poller._logger.error('The service ' + this._serviceId + ' timed out ' +
  65.                                'while refreshing ' + this._urn);
  66.     this._poller._finishedRefresh(this._urn);
  67.   },
  68.   QueryInterface: function PSL_QueryInterface(iid) {
  69.     if (iid.equals(Ci.flockIPollerListener) ||
  70.         iid.equals(Ci.nsITimerCallback) ||
  71.         iid.equals(Ci.nsISupports))
  72.       return this;
  73.     throw Cr.NS_ERROR_NO_INTERFACE;
  74.   }
  75. }
  76.  
  77.  
  78. function PollerService() {
  79.   var obs = getObserverService();
  80.   obs.addObserver(this, 'flock-data-ready', false);
  81.   obs.addObserver(this, 'xpcom-shutdown', false);
  82. }
  83.  
  84. PollerService.prototype = {
  85.   _start: function PS__start() {
  86.     this._logger = Cc['@flock.com/logger;1'].createInstance(Ci.flockILogger);
  87.     this._logger.init('poller');
  88.     this._logger.info('starting up...');
  89.  
  90.     this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
  91.     var evtID = this._profiler.profileEventStart('poller-start');
  92.  
  93.     this._timer = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
  94.  
  95.     this._coop = Cc["@flock.com/singleton;1"]
  96.                  .getService(Ci.flockISingleton)
  97.                  .getSingleton("chrome://flock/content/common/load-faves-coop.js")
  98.                  .wrappedJSObject;
  99.  
  100.     var prefService = Cc['@mozilla.org/preferences-service;1']
  101.       .getService(Ci.nsIPrefBranch2);
  102.     prefService.addObserver(PREF_POLLER_BRANCH, this, false);
  103.  
  104.     this.observe(null, 'nsPref:changed', PREF_REFRESH_DELAY);
  105.     this.observe(null, 'nsPref:changed', PREF_REFRESH_TIMEOUT);
  106.     this.observe(null, 'nsPref:changed', PREF_DEFAULT_REFRESH_INTERVAL);
  107.     this.observe(null, 'nsPref:changed', PREF_MAX_CONCURRENT_REFRESHES);
  108.  
  109.     this._initializeQueue();
  110.     this._watchRDFEvents();
  111.  
  112.     this._profiler.profileEventEnd(evtID, "");
  113.   },
  114.   _shutdown: function PS__shutdown() {
  115.     var prefService = Cc['@mozilla.org/preferences-service;1']
  116.       .getService(Ci.nsIPrefBranch2);
  117.     prefService.removeObserver(PREF_POLLER_BRANCH, this);
  118.  
  119.     this._timer.cancel();
  120.     this._timer = null;
  121.   },
  122.  
  123.   _prefChanged: function PS__prefChanged(pref) {
  124.     var prefService = Cc['@mozilla.org/preferences-service;1']
  125.       .getService(Ci.nsIPrefService);
  126.     var prefBranch = prefService.getBranch(PREF_POLLER_BRANCH);
  127.  
  128.     switch (pref) {
  129.       case PREF_REFRESH_DELAY:
  130.         this._refreshDelay = prefBranch.getIntPref(pref);
  131.         break;
  132.  
  133.       case PREF_REFRESH_TIMEOUT:
  134.         this._refreshTimeout = prefBranch.getIntPref(pref);
  135.         break;
  136.  
  137.       case PREF_DEFAULT_REFRESH_INTERVAL:
  138.         this._defaultRefreshInterval = prefBranch.getIntPref(pref);
  139.         break;
  140.  
  141.       case PREF_MAX_CONCURRENT_REFRESHES:
  142.         this._maxConcurrentRefreshes = prefBranch.getIntPref(pref);
  143.         break;
  144.     }
  145.   },
  146.  
  147.   _watchRDFEvents: function PS__watchRDFEvents() {
  148.     var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
  149.       .getService(Ci.nsIRDFService);
  150.     var nextRefresh = RDFS.GetResource('http://flock.com/rdf#nextRefresh');
  151.     var isPollable = RDFS.GetResource('http://flock.com/rdf#isPollable');
  152.  
  153.     var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
  154.       .getService(Ci.flockIRDFObservable);
  155.     faves.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null, nextRefresh, null,
  156.                          this);
  157.     faves.addArcObserver(Ci.flockIRDFObserver.TYPE_ALL, null, isPollable, null,
  158.                          this);
  159.   },
  160.   rdfChanged: function PS_rdfChanged(ds, type, rsrc, pred, obj, oldObj) {
  161.     var pollable = this._coop.get_from_resource(rsrc);
  162.  
  163.     if (pollable && pollable.isPollable) {
  164.       if (pollable.nextRefresh)
  165.         this._addToQueue(pollable);
  166.       else
  167.         pollable.nextRefresh = new Date();
  168.     } else {
  169.       rsrc.QueryInterface(Ci.nsIRDFResource);
  170.       this._removeFromQueue(rsrc.ValueUTF8);
  171.     }
  172.   },
  173.  
  174.   _initializeQueue: function PS__initializeQueue() {
  175.     var queue = [];
  176.     var dates = {};
  177.  
  178.     var RDFS = Cc['@mozilla.org/rdf/rdf-service;1']
  179.       .getService(Ci.nsIRDFService);
  180.     var isPollable = RDFS.GetResource('http://flock.com/rdf#isPollable');
  181.  
  182.     var faves = Cc['@mozilla.org/rdf/datasource;1?name=flock-favorites']
  183.       .getService(Ci.nsIRDFDataSource);
  184.  
  185.     var pollables = faves.GetSources(isPollable, RDFS.GetLiteral('true'), true);
  186.     while (pollables.hasMoreElements()) {
  187.       var pollable = this._coop.get_from_resource(pollables.getNext());
  188.  
  189.       var urn = pollable.id();
  190.       queue.push(urn);
  191.  
  192.       if (!pollable.nextRefresh)
  193.         pollable.nextRefresh = new Date();
  194.  
  195.       dates[urn] = pollable.nextRefresh;
  196.     }
  197.  
  198.     function sorter(a, b) {
  199.       return dates[a] - dates[b];
  200.     }
  201.     queue.sort(sorter);
  202.  
  203.     this._queue = queue;
  204.     this._dates = dates;
  205.  
  206.     this._refreshing = {};
  207.     this._refreshOrder = [];
  208.  
  209.     this._lastRefresh = new Date(0);
  210.  
  211.     this._logger.debug('queue initialized: ' + this._queue.toSource());
  212.  
  213.     this._initTimer(STARTUP_DELAY);
  214.   },
  215.  
  216.   _addToQueue: function PS__addToQueue(obj) {
  217.     var urn = obj.id();
  218.     var nextRefresh = obj.nextRefresh;
  219.  
  220.     var date = this._dates[urn];
  221.     if (date) {
  222.       if (date - nextRefresh == 0)
  223.         return;
  224.       else
  225.         this._queue.splice(this._queue.indexOf(urn), 1);
  226.     }
  227.  
  228.     this._dates[urn] = nextRefresh;
  229.  
  230.     for (var i = 0; i < this._queue.length; i++) {
  231.       var date = this._dates[this._queue[i]];
  232.       if (nextRefresh < date) {
  233.         this._queue.splice(i, 0, urn);
  234.         if (i == 0)
  235.           this._recalculateTimer();
  236.  
  237.         this._logger.debug('queue updated: ' + this._queue.toSource());
  238.         return;
  239.       }
  240.     }
  241.  
  242.     this._queue.push(urn);
  243.     if (this._queue.length == 1)
  244.       this._recalculateTimer();
  245.  
  246.     this._logger.debug('queue updated: ' + this._queue.toSource());
  247.   },
  248.  
  249.   _removeFromQueue: function PS__removeFromQueue(urn) {
  250.     var date = this._dates[urn];
  251.     if (!date)
  252.       return;
  253.  
  254.     delete this._dates[urn];
  255.  
  256.     var index = this._queue.indexOf(urn);
  257.     if (index == 0) {
  258.       this._queue.shift();
  259.       this._recalculateTimer();
  260.     } else {
  261.       this._queue.splice(index, 1);
  262.     }
  263.  
  264.     this._logger.debug('queue updated: ' + this._queue.toSource());
  265.   },
  266.  
  267.   _initTimer: function PS__initTimer(interval) {
  268.     this._logger.debug('next refresh: ' + interval / 1000);
  269.     this._timer.initWithCallback(this, interval, Ci.nsITimer.TYPE_ONE_SHOT);
  270.   },
  271.   _recalculateTimer: function PS__recalculateTimer() {
  272.     this._timer.cancel();
  273.  
  274.     if (this._queue.length) {
  275.       var date = this._dates[this._queue[0]];
  276.       var now = new Date();
  277.  
  278.       var interval = date > now ? date - now : 0;
  279.       interval = Math.min(interval, MAX_TIMER_INTERVAL);
  280.  
  281.       if (this._refreshOrder.length) {
  282.         var urn = this._refreshOrder[this._refreshOrder.length - 1];
  283.         var start = this._refreshing[urn].start;
  284.         var delay = (this._refreshDelay * 1000) - (now - start);
  285.         interval = Math.max(interval, delay);
  286.  
  287.         if (this._refreshOrder.length > this._maxConcurrentRefreshes - 1) {
  288.           urn = this._refreshOrder[0];
  289.           start = this._refreshing[urn].start;
  290.           delay = (this._refreshTimeout * 1000) - (now - start);
  291.           interval = Math.max(interval, delay);
  292.         }
  293.       } else {
  294.         var delay = (this._refreshDelay * 1000) - (now - this._lastRefresh);
  295.         interval = Math.max(interval, delay);
  296.       }
  297.  
  298.       this._initTimer(interval);
  299.     }
  300.   },
  301.  
  302.   _finishedRefresh: function PS__finishedRefresh(urn) {
  303.     var index = this._refreshOrder.indexOf(urn);
  304.     if (index == -1)
  305.       return;
  306.  
  307.     var refreshInfo = this._refreshing[urn]
  308.  
  309.     if (refreshInfo.timer) {
  310.       refreshInfo.timer.cancel();
  311.     }
  312.  
  313.     delete this._refreshing[urn];
  314.     this._refreshOrder.splice(index, 1);
  315.  
  316.     var obj = this._coop.get(urn);
  317.     if (!obj)
  318.       return;
  319.  
  320.     obj.isRefreshing = false;
  321.  
  322.     var now = new Date();
  323.     if (obj.nextRefresh < now) {
  324.       var refreshInterval = obj.refreshInterval ? obj.refreshInterval
  325.                                                 : this._defaultRefreshInterval;
  326.  
  327.       obj.nextRefresh = new Date(now.getTime() + refreshInterval * 1000);
  328.     }
  329.  
  330.     this._recalculateTimer();
  331.  
  332.     var obs = getObserverService();
  333.     obs.notifyObservers(null, "refresh-myworld", null);
  334.   },
  335.   _refresh: function PS__refresh(obj) {
  336.     var urn = obj.id();
  337.     if (this._refreshing[urn])
  338.       return;
  339.  
  340.     var serviceId = obj.serviceId;
  341.     this._logger.debug('serviceId = ' + serviceId);
  342.  
  343.     var service = null;
  344.     if (serviceId && Cc[serviceId]) {
  345.       try {
  346.         service = Cc[serviceId].getService(Ci.flockIPollingService);
  347.       }
  348.       catch (e) {
  349.         this._logger.error('Problem getting "' + serviceId + '" as a ' +
  350.                            'flockIPollingService, while trying to refresh ' + 
  351.                            urn);
  352.       }
  353.     }
  354.  
  355.     if (service) {
  356.       this._logger.info('Refreshing ' + urn);
  357.       try {
  358.         var now = new Date();
  359.  
  360.         this._refreshing[urn] = { start: now };
  361.         this._refreshOrder.push(urn);
  362.  
  363.         obj.isRefreshing = true;
  364.  
  365.         var listener = new RefreshListener(this, urn, serviceId);
  366.         service.refresh(urn, listener);
  367.  
  368.         var refreshInfo = this._refreshing[urn];
  369.         if (refreshInfo) {
  370.           var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  371.           refreshInfo.timer = timer;
  372.           timer.initWithCallback(listener, this._refreshTimeout * 1000,
  373.                                  Ci.nsITimer.TYPE_ONE_SHOT);
  374.         }
  375.  
  376.         this._lastRefresh = now;
  377.       }
  378.       catch (e) {
  379.         this._logger.error("Exception while "
  380.                            + serviceId
  381.                            + " was refreshing a stream: "
  382.                            + e);
  383.         this._finishedRefresh(urn);
  384.       }
  385.     } else {
  386.       this._logger.error('Problem getting the service, while trying to ' +
  387.                          'refresh ' + urn);
  388.     }
  389.   },
  390.  
  391.   notify: function PS_notify(timer) {
  392.     var urn = this._queue[0];
  393.     var date = this._dates[urn];
  394.  
  395.     var now = new Date();
  396.     if (now < date) {
  397.       this._recalculateTimer();
  398.       return;
  399.     }
  400.  
  401.     this._queue.shift();  
  402.     delete this._dates[urn];
  403.  
  404.     var obj = this._coop.get(urn);
  405.     if (obj)
  406.       this._refresh(this._coop.get(urn));
  407.     else
  408.       this._logger.warn('trying to refresh nonexistent object: ' + urn);
  409.  
  410.     this._recalculateTimer();
  411.   },
  412.  
  413.   observe: function PS_observe(subject, topic, state) {
  414.     var obs = getObserverService();
  415.  
  416.     switch (topic) {
  417.       case 'flock-data-ready':
  418.         obs.removeObserver(this, 'flock-data-ready');
  419.         this._start();
  420.         break;
  421.  
  422.       case 'xpcom-shutdown':
  423.         obs.removeObserver(this, 'xpcom-shutdown');
  424.         this._shutdown();
  425.         break;
  426.  
  427.       case 'nsPref:changed':
  428.         this._prefChanged(state);
  429.         break;
  430.     }
  431.   },
  432.  
  433.   forceRefresh: function PS_forceRefresh(urn) {
  434.     var obj = this._coop.get(urn);
  435.     if (!obj)
  436.       throw 'URN ' + urn + ' does not exist'; 
  437.  
  438.     obj.nextRefresh = new Date(0);
  439.   },
  440.  
  441.   getInterfaces: function PS_getInterfaces(countRef) {
  442.     var interfaces = [Ci.flockIPollerService, Ci.flockIRDFObserver,
  443.                       Ci.nsITimerCallback, Ci.nsIObserver, Ci.nsIClassInfo,
  444.                       Ci.nsISupports];
  445.     countRef.value = interfaces.length;
  446.     return interfaces;
  447.   },
  448.   getHelperForLanguage: function PS_getHelperForLanguage(language) {
  449.     return null;
  450.   },
  451.   contractID: PS_CONTRACTID,
  452.   classDescription: PS_CLASSNAME,
  453.   classID: PS_CLASSID,
  454.   implementationLanguage: Ci.nsIProgrammingLanguage.JAVASCRIPT,
  455.   flags: Ci.nsIClassInfo.SINGLETON,
  456.  
  457.   QueryInterface: function PS_QueryInterface(iid) {
  458.     if (iid.equals(Ci.flockIPollerService) ||
  459.         iid.equals(Ci.flockIRDFObserver) ||
  460.         iid.equals(Ci.nsITimerCallback) ||
  461.         iid.equals(Ci.nsIObserver) ||
  462.         iid.equals(Ci.nsIClassInfo) ||
  463.         iid.equals(Ci.nsISupports))
  464.       return this;
  465.     throw Cr.NS_ERROR_NO_INTERFACE;
  466.   }
  467. }
  468.  
  469.  
  470. function GenericComponentFactory(ctor) {
  471.   this._ctor = ctor;
  472. }
  473.  
  474. GenericComponentFactory.prototype = {
  475.  
  476.   _ctor: null,
  477.  
  478.   // nsIFactory
  479.   createInstance: function(outer, iid) {
  480.     if (outer != null)
  481.       throw Cr.NS_ERROR_NO_AGGREGATION;
  482.     return (new this._ctor()).QueryInterface(iid);
  483.   },
  484.  
  485.   // nsISupports
  486.   QueryInterface: function(iid) {
  487.     if (iid.equals(Ci.nsIFactory) ||
  488.         iid.equals(Ci.nsISupports))
  489.       return this;
  490.     throw Cr.NS_ERROR_NO_INTERFACE;
  491.   },
  492. };
  493.  
  494. var Module = {
  495.   QueryInterface: function(iid) {
  496.     if (iid.equals(Ci.nsIModule) ||
  497.         iid.equals(Ci.nsISupports))
  498.       return this;
  499.  
  500.     throw Cr.NS_ERROR_NO_INTERFACE;
  501.   },
  502.  
  503.   getClassObject: function(cm, cid, iid) {
  504.     if (!iid.equals(Ci.nsIFactory))
  505.       throw Cr.NS_ERROR_NOT_IMPLEMENTED;
  506.  
  507.     if (cid.equals(PS_CLASSID))
  508.       return new GenericComponentFactory(PollerService);
  509.  
  510.     throw Cr.NS_ERROR_NO_INTERFACE;
  511.   },
  512.  
  513.   registerSelf: function(cm, file, location, type) {
  514.     var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
  515.     cr.registerFactoryLocation(PS_CLASSID, PS_CLASSNAME, PS_CONTRACTID,
  516.                                file, location, type);
  517.  
  518.     var catman = Cc['@mozilla.org/categorymanager;1']
  519.       .getService(Ci.nsICategoryManager);
  520.     catman.addCategoryEntry('flock-startup', PS_CLASSNAME,
  521.                             'service,' + PS_CONTRACTID,
  522.                             true, true);
  523.   },
  524.  
  525.   unregisterSelf: function(cm, location, type) {
  526.     var cr = cm.QueryInterface(Ci.nsIComponentRegistrar);
  527.     cr.unregisterFactoryLocation(PS_CLASSID, location);
  528.   },
  529.  
  530.   canUnload: function(cm) {
  531.     return true;
  532.   },
  533. };
  534.  
  535. function NSGetModule(compMgr, fileSpec)
  536. {
  537.   return Module;
  538. }
  539.